{***************************************************************
    SpyGestion  Copyright (C) 2008 Christophe KOALI <nature2gas@gmail.com>
    
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
****************************************************************}
unit modParsingClassBaseDivTable;

interface

uses Classes, sFonctionsChaines, SysUtils;

(***********
Le but: Savoir quand se ferme vraiment une balise DIV en se basant sur les table

  En particulier lorsque qu'une balise DIV fermante est oubliée OU placée
  au mauvais endroit

  Pour l'oublie de la DIV fermante, un évènement sera appelé en même temps
  que la fermeture du tableau correspondant

  Pour le DIV fermant placé n'importe où, la f


  Le niveau des DIV et TABLE doit être stocké

  DIV ouvrante avec table niveau 0
  TABLE Ouvrante, table niveau 0+1 = 1
*)

type

    TDivTablePower = record
       TableLevel : integer;
       DivLevel   : integer; // unique et inutile...
    end;

    PDivTablePower = ^TDivTablePower;


    TDivTableGestion = class(TList)
    private
        FCurrTableLevel: integer;
        FCurrDivLevel: integer;

        function GetLastPointer: PDivTablePower;
        procedure DeleteLastPointer;

        procedure DoDebug(const Msg: string);
    public
        SkipVerification: boolean;

        {$IFDEF DEBUG_PARSING}
           // informations de débogage
           DebugProc: TsfcProcStatus;
        {$ENDIF}


        // TRUE si le Div de fermeture correspond à un vrai div de fermeture
        // sinon FALSE
        function OnDivEnd: boolean;

        // 1 ou plus si cela correspond également à la fermeture d'un div ou plusieurs div
        // sinon 0
        function OnTableEnd: integer;

        procedure OnDivOpen;
        procedure OnTableOpen;

        procedure Init;


        function AddT(Element: TDivTablePower): PDivTablePower;
        function Delete(Index: integer): boolean; 

        procedure Clear;

        constructor Create;
        destructor Destroy;
        

    end;

implementation

procedure TDivTableGestion.DoDebug(const Msg: string);
begin
    {$IFDEF DEBUG_PARSING}
       if Assigned(DebugProc) then begin
          DebugProc('DivTable - ' + Msg);
       end;
    {$ENDIF}
end;


procedure TDivTableGestion.OnDivOpen;
var cData: TDivTablePower;
begin

   if SkipVerification then begin
      exit;
   end;


   // stock le lvl de la table
   Inc(FCurrDivLevel);

   cData.TableLevel := FCurrTableLevel;
   cData.DivLevel   := FCurrDivLevel;

   AddT(cData);

   
   {$IFDEF DEBUG_PARSING_DIV}
       DoDebug(format('Div open: %d - Table: %d', [FCurrDivLevel, FCurrTableLevel]));
   {$ENDIF}

end;



function TDivTableGestion.OnDivEnd: boolean;
var pe: PDivTablePower;
begin
   // vérifie si le div se ferme dans le meme niveau de table que son ouverture

   Result := true;

   if SkipVerification then begin
      // on saute les vérifications,
      // tous les div de fermeture correspondent donc à des div de fermeture
      exit;
   end;

   {$IFDEF DEBUG_PARSING_DIV}
       DoDebug(format('Div end: %d - Table: %d', [FCurrDivLevel, FCurrTableLevel]));
   {$ENDIF}

   pe := GetLastPointer;

   if pe <> nil then begin
      if pe.TableLevel <> FCurrTableLevel then begin
         // erreur
         Result := false;

         {$IFDEF DEBUG_PARSING_DIV}
             DoDebug(format('Div end: level différent de la table, pas considéré comme une fermeture (d: %d,  t: %d)', [pe.DivLevel, pe.TableLevel]));
         {$ENDIF}

      end;
   end;



   if Result then begin
      DeleteLastPointer;
      Dec(FCurrDivLevel);
   end;

end;

function TDivTableGestion.OnTableEnd: integer;
var pe: PDivTablePower;
begin

   Result := 0;

   if SkipVerification then begin
      // on saute les vérifications,
      // tous les div de fermeture sont fermé correctement,
      // et une fermeture de table ne correspond donc JAMAIS à la fermeture d'un div
      exit;
   end;

   // vérifie si un div ne se ferme pas en meme temps, c'est à dire
   // si il existe un div qui a ete ouvert dans le lvl actuel de table

   if FCurrTableLevel = 0 then begin
      // il n'y a rien à fermer...
      {$IFDEF DEBUG_PARSING_DIV}
          DoDebug(format('Table end, balise table de fermeture superflu, div: %d - Table: %d', [FCurrDivLevel, FCurrTableLevel]));
      {$ENDIF}

      exit;
   end;

   {$IFDEF DEBUG_PARSING_DIV}
       DoDebug(format('Table end, div: %d - Table: %d', [FCurrDivLevel, FCurrTableLevel]));
   {$ENDIF}


   pe := GetLastPointer;

   while pe <> nil do begin
      if pe.TableLevel >= FCurrTableLevel then begin

         {$IFDEF DEBUG_PARSING_DIV}
             DoDebug(format('Table end, found error: div: %d - Table: %d', [pe.DivLevel, pe.TableLevel]));
         {$ENDIF}

         Inc(Result);
         DeleteLastPointer;

      end else begin
         break;
      end;

      pe := GetLastPointer;

   end;


   // Pour finir, on passe au niveau inférieur
   Dec(FCurrTableLevel);

end;

procedure TDivTableGestion.OnTableOpen;
begin
   if SkipVerification then begin
      exit;
   end;


    Inc(FCurrTableLevel);

   
   {$IFDEF DEBUG_PARSING_DIV}
       DoDebug(format('Table open, div: %d - Table: %d', [FCurrDivLevel, FCurrTableLevel]));
   {$ENDIF}

end;




function TDivTableGestion.Delete(Index: integer): boolean;
var pElement: PDivTablePower;
begin

    Result := false;

    if (Index < 0) or (Index >= Count) then
       // Hors limite
       exit;


    // Obtient le pointeur
    pElement := Items[Index];

    if pElement <> nil then
       // Libére la mémoire créée lors de l'ajout
       Dispose(pElement);

    // Supprime le pointeur de la liste
    inherited Delete(Index);

    // Met le resultat à True
    Result := true;

end;



function TDivTableGestion.AddT(Element: TDivTablePower): PDivTablePower;
var pElement: PDivTablePower;
begin
    
    Result := nil;
    
    New(pElement);          // Créer l'espace mémoire requis
    pElement^ := Element;   // Recopie les données dans l'emplacement pointé
    
    try
       inherited Add(pElement);  // Ajoute le pointeur à la liste
       Result := pElement;
    except
       Result := nil;
    end;
    
    // Pour libérer la mémoire occupé par l'élément pointer:  Dispose(pElement);

end;


destructor TDivTableGestion.Destroy;
begin
    // Implémenter le code avant l'héritage de la méthode
    // C'est à dire ici
    
   
    // On appelle NOTRE clear qui lui-même appelle NOTRE Delete qui libère l'espace mémoire
    // avant d'enlever le pointeur de la TList
    // L'héritage de Destroy appelle Clear, mais es-ce le notre ou celui de la classe de base ?
    Clear;
    
    
    // Héritage
    InHerited Destroy;
end;


procedure TDivTableGestion.Clear;
var i: integer;
begin

    if Count > 0 then begin
        For i := Count -1 downto 0 do
            Delete(i);
    end;
    
    inherited Clear;
end;


procedure TDivTableGestion.DeleteLastPointer;
begin
    if Count = 0 then begin
       exit;
    end;

    Delete(Count - 1);
end;

function TDivTableGestion.GetLastPointer: PDivTablePower;
begin
    Result := nil;

    if Count = 0 then begin
       exit;
    end;

    Result := Items[Count - 1];
end;

constructor TDivTableGestion.Create;
begin
    inherited;

    SkipVerification := false;

end;

procedure TDivTableGestion.Init;
begin
   FCurrTableLevel := 0;
   FCurrDivLevel   := 0;
   Clear;
end;

end.
